using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.IO;
using Beaker.OpenCube.Game;
using Beaker.OpenCube.Textures;
using Beaker.Services.Interfaces;
using Beaker.Services.Locator;

namespace Beaker.OpenCube
{
	public interface ITexturePack
	{
		BlockTexture GetBlockTexture( Block block );
		Color GetBiomeColour( BiomeType biomeType, BiomeColor biomeColor );
	}

	/// <summary>
	/// Dynamicly process the requested texture pack data
	/// </summary>
	public sealed class DynamicTexturePack
		: ITexturePack
	{
		private readonly IServiceLocator _serviceLocator;
		private readonly IIOService _ioService;
		private readonly string _texturePackFilePath;
		private readonly Stream _stream;
		private readonly TexturePackReader _reader;

		internal DynamicTexturePack( IServiceLocator serviceLocator, string texturePackFilePath )
		{
			Contract.Requires<ArgumentNullException>( !string.IsNullOrEmpty( texturePackFilePath ) );

			_serviceLocator = serviceLocator ?? ServiceLocator.Current ?? PreInitializedServiceLocator.Initialize();
			_ioService = _serviceLocator.GetInstance<IIOService>();

			if( _ioService == null )
				throw new ArgumentException( "The service locator did not provided an implementation for IIOService", "serviceLocator" );

			_serviceLocator = serviceLocator;
			_texturePackFilePath = texturePackFilePath;

			_stream = File.OpenRead( _texturePackFilePath );
			_reader = TexturePackReader.FromStream( _stream );
		}

		private readonly Dictionary<Block, WeakReference<BlockTexture>> _blockTextures = 
			new Dictionary<Block, WeakReference<BlockTexture>>();

		/// <summary>
		/// Get the texture for the given block
		/// </summary>
		/// <param name="block">The block to get the texture for</param>
		/// <returns>The texture for the requested block</returns>
		public BlockTexture GetBlockTexture( Block block )
		{
			// check if there is already a dictionary item
			WeakReference<BlockTexture> possibleBlockTexture;
			if( !_blockTextures.TryGetValue( block, out possibleBlockTexture ) )
			{
				// create a new dictionary item.
				// load block texture
				var blockTexture = _reader.ReadBlockTexture( block );
				// create weak reference for it
				possibleBlockTexture = new WeakReference<BlockTexture>( blockTexture );
				// add to dictionary
				_blockTextures.Add( block, possibleBlockTexture );

				return blockTexture;
			}

			// there is already a dictionary item, so get, or recreate
			return possibleBlockTexture.GetOrCreate( () => _reader.ReadBlockTexture( block ) );
		}
	}
}